/*******************************************************************************
 * This header file contains all the classes of the lexical analyzer.
 *
 * Lexical analyzer is the first process of sbasic language interpreter
 *
 * Contains the definitions of various identifiers and an identifier processor
 *
 * @author LoveLonelyTime
 * @version 1.1
 * @date 2021-2021
 * @copyright GNU Public License.
 ******************************************************************************/

#ifndef LEXER_H_INCLUDED
#define LEXER_H_INCLUDED

#include <iostream>
#include <string>

#include "language.h"

namespace SBASIC
{
    /**
     * The Token for source code.
     */
    class Token
    {
    private:
        /**
         * The line number of the Token place.
         */
        line_number m_line_number;

    public:
        /**
         * Created by line number
         * @param[in] ln The line number of the Token place.
         */
        Token(line_number ln) : m_line_number(ln){};

        /**
         * Get line number.
         * @return The line number.
         */
        line_number get_line_number() const
        {
            return m_line_number;
        }

        /**
         * Get type of the token.
         * This is a pure virtual function, implemented by subclass.
         * @return The token type.
         */
        virtual TOKEN get_token_type() const = 0;
        virtual ~Token() {}
    };

    /**
     * DecimalToken Class, inherited from Token.
     * Represent a number.
     */
    class DecimalToken : public Token
    {
    private:
        /**
         * The content of decimal.
         */
        sbasic_decimal_type m_decimal;

    public:
        /**
         * Created by line number, decimal.
         * @param[in] ln The line number of the Token place.
         * @param[in] decimal The decimal.
         */
        DecimalToken(line_number ln, sbasic_decimal_type decimal) : Token(ln), m_decimal(decimal) {}

        /**
         * Get decimal.
         * @return The decimal.
         */
        sbasic_decimal_type get_decimal() const
        {
            return m_decimal;
        }

        /**
         * Get token type.
         * @return The DECIMAL_TOKEN.
         */
        TOKEN get_token_type() const override
        {
            return TOKEN::DECIMAL_TOKEN;
        }
    };

    /**
     * IdentifierToken Class, inherited from Token.
     * Represent a identifier.
     */
    class IdentifierToken : public Token
    {
    private:
        /**
         * The content of identifier.
         */
        std::string m_identifier;

    public:
        /**
         * Created by line number, id.
         * @param[in] ln The line number of the Token place.
         * @param[in] id The identifier.
         */
        IdentifierToken(line_number ln, const std::string &id) : Token(ln), m_identifier(id) {}

        /**
         * Get identifier.
         * @return The identifier.
         */
        std::string get_identifier() const
        {
            return m_identifier;
        }

        /**
         * Get token type.
         * @return The IDENTIFIER_TOKEN.
         */
        TOKEN get_token_type() const override
        {
            return TOKEN::IDENTIFIER_TOKEN;
        }
    };

    /**
     * NewlineToken Class, inherited from Token.
     * Represent a new line.
     */
    class NewlineToken : public Token
    {
    public:
        /**
         * Created by line number.
         * @param[in] ln The line number of the Token place.
         */
        NewlineToken(line_number ln) : Token(ln) {}

        /**
         * Get token type.
         * @return The NEWLINE_TOKEN.
         */
        TOKEN get_token_type() const override
        {
            return TOKEN::NEWLINE_TOKEN;
        }
    };

    /**
     * StringToken Class, inherited from Token.
     * Represent a string.
     */
    class StringToken : public Token
    {
    private:
        /**
         * The content of string.
         */
        std::string m_string;

    public:
        /**
         * Created by line number, string.
         * @param[in] ln The line number of the Token place.
         * @param[in] str The string.
         */
        StringToken(line_number ln, const std::string &str) : Token(ln), m_string(str) {}

        /**
         * Get string.
         * @return The string.
         */
        std::string get_string() const
        {
            return m_string;
        }

        /**
         * Get token type.
         * @return The STRING_TOKEN.
         */
        TOKEN get_token_type() const override
        {
            return TOKEN::STRING_TOKEN;
        }
    };

    /**
     * KeywordToken Class, inherited from Token.
     * Represent a keyword.
     */
    class KeywordToken : public Token
    {
    private:
        /**
         * The type of keyword.
         */
        KEYWORD m_keyword;

    public:
        /**
         * Created by line number, keyword.
         * @param[in] ln The line number of the Token place.
         * @param[in] keyword The keyword.
         */
        KeywordToken(line_number ln, KEYWORD keyword) : Token(ln), m_keyword(keyword) {}

        /**
         * Get keyword type.
         * @return The keyword type.
         */
        KEYWORD get_keyword() const
        {
            return m_keyword;
        }

        /**
         * Get token type.
         * @return The KEYWORD_TOKEN.
         */
        TOKEN get_token_type() const override
        {
            return TOKEN::KEYWORD_TOKEN;
        }
    };

    /**
     * EOFToken Class, inherited from Token.
     * Represent a eof(end of file).
     */
    class EOFToken : public Token
    {
    public:
        /**
         * Created by line number.
         * @param[in] ln The line number of the Token place.
         */
        EOFToken(line_number ln) : Token(ln) {}

        /**
         * Get token type.
         * @return The EOF_TOKEN.
         */
        TOKEN get_token_type() const override
        {
            return TOKEN::EOF_TOKEN;
        }
    };

    /**
     * OperatorToken Class, inherited from Token.
     * Represent a operator.
     */
    class OperatorToken : public Token
    {
    private:
        /**
         * The type of operator.
         */
        OPERATOR m_operator;

    public:
        /**
         * Created by line number, operator.
         * @param[in] ln The line number of the Token place.
         * @param[in] op The operator.
         */
        OperatorToken(line_number ln, OPERATOR op) : Token(ln), m_operator(op) {}

        /**
         * Get operator type.
         * @return The operator type.
         */
        OPERATOR get_operator() const
        {
            return m_operator;
        }

        /**
         * Get token type.
         * @return The OPERATOR_TOKEN.
         */
        TOKEN get_token_type() const override
        {
            return TOKEN::OPERATOR_TOKEN;
        }
    };

    /**
     * DelimiterToken Class, inherited from Token.
     * Represent a delimiter.
     */
    class DelimiterToken : public Token
    {
    private:
        /**
         * The type of delimiter.
         */
        DELIMITER m_delimiter;

    public:
        /**
         * Created by line number, delimiter.
         * @param[in] ln The line number of the Token place.
         * @param[in] delimiter The delimiter.
         */
        DelimiterToken(line_number ln, DELIMITER delimiter) : Token(ln), m_delimiter(delimiter) {}

        /**
         * Get delimiter type.
         * @return The delimiter type.
         */
        DELIMITER get_delimiter() const
        {
            return m_delimiter;
        }

        /**
         * Get token type.
         * @return The DELIMITER_TOKEN.
         */
        TOKEN get_token_type() const override
        {
            return TOKEN::DELIMITER_TOKEN;
        }
    };

    /**
     * TokenReader Class.
     * This is an essential tool,reading Token from source code.
     */
    class TokenReader
    {
    private:
        /**
         * The input stream.
         */
        std::istream &m_is;

        /**
         * The current character.
         */
        char m_current_char;

        /**
         * The current line number.
         */
        line_number m_line_number;

        /**
         * Read a character.
         */
        void read_char();

        /**
         * Read a digit.
         * @return A digit.
         */
        std::string read_digit();

        /**
         * Read a string.
         * @return A string.
         */
        std::string read_string();

        /**
         * Read a word.
         * @return A word.
         */
        std::string read_word();

    public:
        /**
         * Create a TokenReader by an input stream.
         */
        TokenReader(std::istream &is);

        /**
         * Read next Token.
         * @return The pointer of Token.
         */
        Token *read_token();

        /**
         * delete the Token,got by read_token().
         * @param[in] token_ptr The pointer of Token.
         */
        void delete_token(Token *token_ptr)
        {
            delete token_ptr;
        }
    };
}

#endif // LEXER_H_INCLUDED
